#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
#include <time.h>
#include <syslog.h>
#include <signal.h>
#include <errno.h>
#include <sys/prctl.h>

#include "cms_log.h"


char cmsModuleName[16] = {0};
char pName[16] = {0};

int cmsLogLevel = LL_WARNING;

int cmsLogSyslog = 0;

static char logLevelMsg[][32] = {
    "FATAL",
    LL_COLOR_RED"ERROR"LL_COLOR_NON,
    LL_COLOR_BLU"WARNI"LL_COLOR_NON,
    "NOTIC",
    "INFOR",
    "DEBUG",
    "SPEW ",
    "FLOOD",
    LL_COLOR_GRE"PRINT"LL_COLOR_NON
};


void log_enable_syslog(void)
{
    if(!cmsLogSyslog){
        cmsLogSyslog = 1;
    }
}

void log_disable_syslog(void)
{
    if(cmsLogSyslog){
        cmsLogSyslog = 0;
    }
}

static int level_to_syslog_level(int level)
{
    int result = level + LOG_CRIT;
    if(result > LOG_DEBUG)
        result = LOG_DEBUG;

    return result;
}

void set_log_level(char *moduleName, int level)
{
    if(moduleName){
        strncpy(cmsModuleName, moduleName, sizeof(cmsModuleName) - 1);
    }
    
    if((level >= LL_FATAL) && (level <= LL_FLOOD)){
        cmsLogLevel = level;
    }
}


void cms_log_print(const char *fileName, const char *funcName, int lineNum, enum loglevel level, const char *fmt, ...)
{
    va_list ap;
    char fs[256] = {0};
    time_t t;
    char timeStr[32] = {0};
    char timeMs[8] = {0};
    int syslogFlag = level & LL_SYSLOG_MASK;

    level = level & LL_LEVEL_MASK;
    if((level != LL_PRINT) && (level > cmsLogLevel)){
        return;
    }

    if(pName[0] == '\0'){
        if(prctl(PR_GET_NAME, (unsigned long)pName) != 0){
            strncpy(pName, cmsModuleName, sizeof(pName) - 1);
        }
    }
    
#if CMS_LOG_SUPPORT_MS
    struct timeval tv;
    if(access(CMS_LOG_MS_FILE, F_OK) == 0){
        gettimeofday(&tv,NULL);
        snprintf(timeMs, sizeof(timeMs) - 1, ".%3d", (int32_t)(tv.tv_usec/1000));
    }
#endif
    t = time(NULL);
    strftime(timeStr, sizeof(timeStr), "%Y-%m-%d %H:%M:%S", localtime(&t));
    
    snprintf(fs, sizeof(fs) - 1, "[%s%s][%s][%s][%s][%s:%d:%s]%s\n", 
            timeStr, timeMs, CMS_LOG_PREFIX, logLevelMsg[level], pName, fileName, lineNum, funcName, fmt);
        
    va_start(ap, fmt);
    vfprintf(stderr, fs, ap);
    va_end(ap);
        
    if(cmsLogSyslog && syslogFlag){
        if(level <= LL_ERROR){
            va_start(ap, fmt);
            sprintf(fs, "[%s:%s:%d] %s\n", fileName, funcName, lineNum, fmt);
            vsyslog(level_to_syslog_level(level), fs, ap);
            va_end(ap);
        }
    }
}

void cms_syslog_print(const char *fileName, const char *funcName, int lineNum, int prority, const char *fmt, ...)
{
    va_list ap;
    char fs[256] = {0};
    struct timeval tv;
    char time[32];    
    
    if(pName[0] == '\0'){
        if(prctl(PR_GET_NAME, (unsigned long)pName) != 0){
            strncpy(pName, cmsModuleName, sizeof(pName) - 1);
        }
    }
    
    gettimeofday(&tv,NULL);
    snprintf(time,sizeof(time),"%d.%d", (int32_t)tv.tv_sec, (int32_t)tv.tv_usec/1000);

    sprintf(fs, "%s %s[%s:%s:%d]%s\n", time, pName, fileName, funcName, lineNum, fmt);
    va_start(ap, fmt);
    vsyslog(prority, fs, ap);
    va_end(ap);
}

int cms_log_level_check(int level)
{
    if(level > cmsLogLevel){
        return 0;
    }
    
    return 1;
}

